package com.yyfax.ph.common.utils.crashutils;

import android.annotation.SuppressLint;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.os.Looper;
import android.text.TextUtils;
import com.yyfax.common.CLog;
import com.yyfax.ph.common.GlobalVariableConfig;
import com.yyfax.ph.common.utils.Constants;
import com.yyfax.ph.main.MainActivity;

import java.io.*;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Title: CrashHandler Description: Company: YYLending(YouJingPuHui)
 *
 * @author XiongYuFeng
 * date 2016年9月28日 下午4:39:13
 */
public class CrashHandler implements UncaughtExceptionHandler {
	private final String TAG = CrashHandler.class.getSimpleName();

	private WeakReference<Context> mReference;

	private UncaughtExceptionHandler mDefaultHandler;
	private OnCrashListener mOnCrashListener;

	private StringBuilder mCrashReport = new StringBuilder();

	private String mPath;

	private static CrashHandler mInstance;

	/**
	 * 非单例模式
	 */
	public static CrashHandler getInstance(Context ctx) {
		if (mInstance == null) {
			mInstance = new CrashHandler(ctx);
		}
		return mInstance;
	}

	private CrashHandler(Context ctx) {
		mReference = new WeakReference<>(ctx);
		if (checkSDCard()) {
			mPath = Environment.getExternalStorageDirectory().getPath() + File.separator + ctx.getPackageName();
		} else {
			mPath = ctx.getFilesDir().getParent();
		}
		mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
		Thread.setDefaultUncaughtExceptionHandler(this);
	}

	@Override
	public void uncaughtException(Thread thread, Throwable ex) {
		if (mReference == null) return;
		Context ctx = mReference.get();

		if (!handlerException(ctx, ex) && mDefaultHandler != null) {
			// 如果系统出现未捕获的异常，mDefaultHandler将处理它
			mDefaultHandler.uncaughtException(thread, ex);
		} else {
			if (ctx != null) {
				Intent intent = new Intent(ctx, MainActivity.class);
				intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
				PendingIntent restartIntent = PendingIntent.getActivity(ctx, 0, intent, 0);

				AlarmManager mgr = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
				mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 600, restartIntent);
			}
		}
	}

	/**
	 * 出现未捕获的异常的处理方法
	 *
	 * @param ex 异常
	 * @return boolean
	 */
	private boolean handlerException(final Context ctx, Throwable ex) {
		if (ex == null) {
			return true;
		}

		final String msg = ex.getLocalizedMessage();
		if (TextUtils.isEmpty(msg)) {
			return false;
		}
		CLog.e(TAG, msg);

		if (ctx != null) {
			GlobalVariableConfig.getExecutorService().execute(new Runnable() {
				@Override
				public void run() {
					Looper.prepare();
					// 当出现异常是，友好处理
					if (mOnCrashListener != null) {
						mOnCrashListener.onCrashDialog(ctx);
					}
					Looper.loop();
				}
			});

			// 收集错误信息
			collectCrashDeviceInfo(ctx);
			// 保持错误报告到本地
			saveCrashInfo(ex);
			// 发送错误报告到服务器，并删除本地文件
			sendCrashReport();
			ex.printStackTrace();
			return true;
		}
		return false;
	}

	/**
	 * 当系统crash的时候，收集错误信息
	 */
	private void collectCrashDeviceInfo(Context ctx) {
		try {
			PackageManager pm = ctx.getPackageManager();
			PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
			if (pi != null) {
				mCrashReport.append("\n\t");
				mCrashReport.append("versionName").append(" = ").append(String.valueOf(pi.versionName)).append("\n\t");
				mCrashReport.append("versionCode").append(" = ").append(String.valueOf(pi.versionCode)).append("\n\t");
			}

			// 根据反射获取Build的所有信息
			Field[] fieldList = Build.class.getDeclaredFields();
			if (fieldList != null) {
				for (Field device : fieldList) {
					device.setAccessible(true);
					mCrashReport.append(device.getName()).append(" = ").append(String.valueOf(device.get(null))).append("\n\t");
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 保存错误报告到本地
	 *
	 * @param ex 异常信息
	 */
	private void saveCrashInfo(Throwable ex) {
		try {
			Writer writer = new StringWriter();
			PrintWriter printWriter = new PrintWriter(writer);
			ex.printStackTrace(printWriter);

			Throwable cause = ex.getCause();
			while (cause != null) {
				cause.printStackTrace(printWriter);
				cause = cause.getCause();
			}

			String result = writer.toString();
			printWriter.close();

			mCrashReport.append("\n\t");
			mCrashReport.append(ex.getMessage()).append("\n\t");
			mCrashReport.append(result).append("\n\t");

			String fileName = getCrashFileName();
			File file = new File(mPath);
			if (!file.exists() && !file.mkdirs()) {
                //如果没有创建成功
                throw new Exception();
				//file.mkdirs();
			}
			file = new File(mPath, fileName);
			CLog.e(TAG, file.getPath());

			FileOutputStream fos = new FileOutputStream(file);
			fos.write(mCrashReport.toString().getBytes());
			fos.flush();
			fos.close();

		} catch (Exception e) {
			if (Constants.IS_DEBUG)
				e.printStackTrace();
		}
	}

	/**
	 * 判断SDCard是否存在,并可写
	 *
	 * @return boolean
	 */
	private boolean checkSDCard() {
		String flag = Environment.getExternalStorageState();
		return android.os.Environment.MEDIA_MOUNTED.equals(flag);
	}

	/**
	 * 获取错误报告文件名称
	 *
	 * @return String
	 */
	@SuppressLint("SimpleDateFormat")
	private String getCrashFileName() {
		Date date = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
		return "crash_" + sdf.format(date) + ".txt";
	}

	/**
	 * 发送错误日志报告，并删除本地文件
	 */
	public void sendCrashReport() {
	}

	public OnCrashListener getOnCrashListener() {
		return mOnCrashListener;
	}

	public void setOnCrashListener(OnCrashListener listener) {
		mOnCrashListener = listener;
	}
}
